iT邦幫忙

2022 iThome 鐵人賽

DAY 19
0
Mobile Development

Android app 效能優化系列 第 19

使用 LeakCanary 找出 Memory leak

  • 分享至 

  • xImage
  •  

LeakCanary 是 Square 公司所開發出來的檢測 Memory leak 的工具。不需要撰寫程式碼就可以自動偵測 Activity 與 Fragment 是否 Memory leak。

首先加入 dependencies

debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1'

除了自動測偵的項目,你也可以使用 AppWatcher.objectWatcher.watch 加上想監控的物件。

AppWatcher.objectWatcher.watch(myView, "View was detached")

我們把之前 Memory leak 篇所提到的範例來測試看看。這個範例 Activity 被一個 Singleton 所參考了,在 Activity 離開時無法釋放資源造成 Memory leak。

class MainActivity2 : AppCompatActivity() {
    private val binding by lazy { ActivityMain2Binding.inflate(layoutInflater) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        //Activity 被一個 Singleton 參考
        Class1.context = this
    }
}

object Class1 {
    lateinit var context: Context
}

由於 LeakCanary 是自動偵測,所以直接執行 App 即可開始偵測。當偵測到 Memory leak,即會用通知的方式顯示。
https://ithelp.ithome.com.tw/upload/images/20221004/20111896LxTTPaZLLJ.png

點下通知,可以看到如下圖左邊畫面,是一個跟 Class1.context 有關的 Memory leak。再點進去可以看到下圖右邊畫面表示 Memory leak 發生在 MainActivity2,並會以紅色錯誤底線~~~來標示 Memory leak 的原因。
https://ithelp.ithome.com.tw/upload/images/20221004/20111896lgZofolhdW.png

從 Logcat 也可以看到詳細的 Log。

====================================
HEAP ANALYSIS RESULT
====================================
1 APPLICATION LEAKS

evan.chen.toturial.memoryleaksample.MainActivity2 instance
   Leaking: YES (ObjectWatcher was watching this because evan.chen.toturial.memoryleaksample.MainActivity2 received
   Activity#onDestroy() callback and Activity#mDestroyed is true)
   Retaining 90.0 kB in 1473 objects
   key = c2b015e6-8494-485c-9a73-40e7c876681b
   watchDurationMillis = 101708
   retainedDurationMillis = 96703
   mApplication instance of android.app.Application
   mBase instance of androidx.appcompat.view.ContextThemeWrapper

再看另一個範例 Activity 未取消註冊 Broadcast 造成 Memory leak。

class MainActivity5 : AppCompatActivity() {
    private val binding by lazy { ActivityMain5Binding.inflate(layoutInflater) }

    private lateinit var broadcastReceiver: MyBroadcastReceiver

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        broadcastReceiver = MyBroadcastReceiver()

    }

    override fun onResume() {
        super.onResume()

        val intentFilter = IntentFilter("evan.chen.tutorial.broadcastsample.Action1")
        registerReceiver(broadcastReceiver, intentFilter)
    }

    override fun onPause() {
        super.onPause()

				//unregisterReceiver(broadcastReceiver)
    }
}

執行後一樣會收到通知,點選通知可以看到 Memory leak 發生在 MainActivity5。
https://ithelp.ithome.com.tw/upload/images/20221004/20111896xOK3k1r961.png

LeakCanary 用來測試是否有 Memory leak,可以一邊操作 App 一邊看是否有錯誤訊息。Profiler 則是必須 dump heap 才能發現是否有 Memory leak,所以兩個的功能還是有些差異。那麼 LeakCanary 是怎麼做到的呢?當 Activity 或 Fragment 被 destroy 後,ObjectWatcher 就會用 WeakReference 的方式參考著這些 Instance,在 GC 發生的幾秒內,如果這些參考沒有被移除,就被視為 Memory leak。

在記憶體的使用上,除了要避免 Memory leak ,還有其他要注意與分析的。在下一篇將接著介紹 Profiler 分析 App 的記憶體使用。


上一篇
使用 Coroutine 減少 Memory leak
下一篇
使用 Profiler 找出記憶體的異常使用
系列文
Android app 效能優化30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言